#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "VObjectText.h"
#include "Class.h"
#include "TextView.h"
#include "TextMarks.h"
#include "Mark.h"
#include "SortedOList.h"
#include "Filler.h"
#include "Invariant.h"
#include "ET_stdio.h"

const cMarkChar = '#'; // character to mark a VisualMark

static TextChanges changeRec;

InvariantChecker(VObjectText);

//---- list to keep VisualMarks sorted according their position ----------------

class VisualMarkList : public SortedObjList {
public:
	VisualMarkList()
		{ }
	int CompareObjects(Object*, Object*);
};

int VisualMarkList::CompareObjects(Object *op1, Object *op2)
{
	return ((Mark*)op1)->Pos() - ((Mark*)op2)->Pos();
}

//----- class VObjectText ------------------------------------------------------

NewMetaImpl(VObjectText,StyledText, (TP(visualMarks), TP(tv)));

void VObjectText::ClassInvariant()
{
	if (GetAssertLevel() > 7)
		return;
	// every VisualMark is marked in the text by a cMarkChar
	Iter next(visualMarks->MakeIterator());
	Mark *m;
	while (m= (Mark*)next())
		if (CharAt(m->Pos()) != cMarkChar)
			fprintf(stderr, "!!!VObjectText Invariant: Marks out of sync %d %c\n",
				m->Pos(), CharAt(m->Pos()));
}

VObjectText::VObjectText()
{
	Init();
}

VObjectText::VObjectText(int sz, Font *fd, TxtParaAlign al)
												: StyledText(sz, fd, al)
{
	Init();
}

VObjectText::VObjectText(byte *buf, int len, Font *fd, TxtParaAlign al)
												: StyledText(buf, len, fd, al)
{
	Init();
}

void VObjectText::Init()
{
	visualMarks= new MarkList(TRUE, new VisualMarkList);
}

VObjectText::~VObjectText()
{
	if (visualMarks)
		SafeDelete(visualMarks);
}

void VObjectText::InitNew()
{
	StyledText::InitNew();
	Init();
}

TextView *VObjectText::GetTextView()
{
	return tv;
}

void VObjectText::ReplaceRange(int from, int to, Text *src, int sfrom, int sto)
{
	DoDelayChanges dc(this);
	dc.Use();
	AssertInvariant(VObjectText);

	visualMarks->Replace(from, to, sto-sfrom);
	if (src->IsKindOf(VObjectText)) { // paste the gobjects and their marks
		VObjectText *vt= (VObjectText *)src;
		register VisualMark *mp, *nmp;
		
		Iter next(vt->visualMarks->MakeIterator());
		while (mp= (VisualMark*)next()) {
			if (mp->pos >= sfrom && mp->pos+mp->len <= sto) {
				nmp= (VisualMark*)mp->DeepClone();
				if (tv)
					nmp->SetContainer(tv);
				nmp->pos+= from-sfrom;
				visualMarks->Add(nmp);
			}
		}
	}
	StyledText::ReplaceRange(from, to, src, sfrom, sto);
}

void VObjectText::SetCharStyle(TxtCharProp m, int from, int to, const CharStyleSpec &st)
{
	register VisualMark *mp;
	CharStyleChange cp;
	cp.what= m;
	cp.st= st;

	Iter next(visualMarks->MakeIterator());
	while (mp= (VisualMark*)next())
		if (mp->pos >= from && mp->pos+mp->len <= to)
			mp->SendDown(cIdNone, cPartCharStyleChanged, &cp);
	StyledText::SetCharStyle(m, from, to, st);
}

void VObjectText::ReplaceCharStyles(RunArray *styles, int from, int to)
{
	StyledText::ReplaceCharStyles(styles, from, to);
	register VisualMark *mp;

	Iter next(visualMarks->MakeIterator());
	while (mp= (VisualMark*)next())
		if (mp->pos >= from && mp->pos+mp->len <= to)
			mp->SendDown(cIdNone, cPartCharStyleRepl, GetCharStyle(mp->pos));
}

void VObjectText::PasteVObject(VObject *vop, int from, int to)
{
	PasteVisualMark(new VObjectMark(vop), from, to);
}

void VObjectText::PasteVisualMark(VisualMark *vmp, int from, int to)
{
	AssertInvariant(VObjectText);
	DoDelayChanges dc(this);
	dc.Use();

	vmp->SetContainer(tv);
	vmp->CalcExtent();
	vmp->SendDown(0, cPartEnableLayoutCntl, 0);
	tv->GetSelection(&from, &to);
	vmp->ChangeMark(from, 1);
	Insert(cMarkChar, from, to);
	visualMarks->Add(vmp);
}

void VObjectText::SetView(TextView *vp)
{
	Iter next(visualMarks->MakeIterator());
	register VisualMark *m;

	tv= vp;
	while (m= (VisualMark*) next())
		m->SetContainer(vp);
}

VisualMark *VObjectText::GetVisualMarkAt(int charNo)
{
	register VisualMark *v= 0;
	Iter next(visualMarks->MakeIterator());

	while (v= (VisualMark*) next())
		if (v->pos == charNo && v->len == 1)
			break;
	return v;
}

int VObjectText::FindPositionOfVObject(VObject *gop1)
{
	register Mark *mp;
	int pos= -1;
	VObject *vop= 0;
	Iter next(visualMarks->MakeIterator());

	while (mp= (Mark*)next()) {
		if (mp->IsKindOf(VObjectMark)) {
			vop= ((VObjectMark*)mp)->GetVObject();
			if (vop == gop1)
				return mp->Pos();
		}
	}
	return pos;
}

byte VObjectText::GetMarkChar()
{
	return cMarkChar;
}

VObject *VObjectText::VObjectAt(int at)
{
	VisualMark *vmp= GetVisualMarkAt(at);
	if (vmp->IsKindOf(VObjectMark))
		return ((VObjectMark*)vmp)->GetVObject();
	return 0;
}

void VObjectText::VObjectChangedSize(VObject *vop)
{
	int at= FindPositionOfVObject(vop);
	if (at == -1)
		return; //Error("VObjectChangedSize", "vobject not found");
	Send(cIdNone, eTextReplaced, changeRec(at, at+1, 1));
}

Iterator *VObjectText::VisualMarksIterator()
{
	return visualMarks->MakeIterator();
}

int VObjectText::VisualMarksCount()
{
	return visualMarks->Size();
}

MarkList *VObjectText::GetVisualMarks()
{
	return visualMarks;
}

OStream &VObjectText::PrintOn(OStream &s)
{
	AssertInvariant(VObjectText);
	StyledText::PrintOn(s);
	return s << visualMarks SP;
}

IStream &VObjectText::ReadFrom(IStream &s)
{
	AssertInvariant(VObjectText);
	StyledText::ReadFrom(s);
	s >> visualMarks;
	return s;
}

IStream& VObjectText::ReadFromAsPureText(IStream &s, long sizeHint)
{
	StyledText::ReadFromAsPureText(s, sizeHint);
	SafeDelete(visualMarks);
	Init();
	return s;
}

void VObjectText::CollectParts(Collection *col)
{
	StyledText::CollectParts(col);
	col->Add(visualMarks);
}

